using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.ObjectModel;

namespace Microsoft.VSPowerToys.PackInstallerDataModel
{
    /// <summary>
    /// Represents an Atom Feed schema
    /// </summary>
    [XmlRoot(ElementName="feed", Namespace="http://www.w3.org/2005/Atom")]
    [Serializable]
    public class AtomFeed
    {

        private Collection<AtomPerson> authors = new Collection<AtomPerson>();

        /// <summary>
        /// Authors for the feed
        /// </summary>
        [XmlElement("author")]
        public Collection<AtomPerson> Authors
        {
            get { return authors; }
            set { authors = value; }
        }

        private string title;

        /// <summary>
        /// Title of the Atom feed
        /// </summary>
        [XmlElement("title")]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        private Collection<AtomCategory> categories = new Collection<AtomCategory>();

        /// <summary>
        /// Categories assigned to this feed
        /// </summary>
        [XmlElement("category")]
        public Collection<AtomCategory> Categories
        {
            get { return categories; }
            set { categories = value; }
        }

        private DateTime lastUpdate;

        /// <summary>
        /// Time of last update to the feed
        /// </summary>
        [XmlElement("updated", IsNullable = false, Type = typeof(DateTime))]
        public DateTime LastUpdate
        {
            get { return lastUpdate; }
            set { lastUpdate = value; }
        }

        private string id;

        /// <summary>
        /// Identified for the feed
        /// </summary>
        [XmlElement("id", IsNullable = false)]
        public string Id
        {
            get { return id; }
            set { id = value; }
        }

        private Collection<AtomLink> links = new Collection<AtomLink>();

        /// <summary>
        /// Links related to this feed
        /// </summary>
        [XmlElement("link")]
        public Collection<AtomLink> Links
        {
            get { return links; }
            set { links = value; }
        }

        private Collection<AtomItem> items = new Collection<AtomItem>();

        /// <summary>
        /// Gets or sets the items included in this feed
        /// </summary>
        [XmlElement("entry")]
        public Collection<AtomItem> Items
        {
            get { return items; }
            set { items = value; }
        }

        private AtomContent copyright;

        /// <summary>
        /// Gets or sets the copyright information for the feed file
        /// </summary>
        [XmlElement("rights")]
        public AtomContent Copyright
        {
            get { return copyright; }
            set { copyright = value; }
        }

        private AtomContent subtitle;

        /// <summary>
        /// Gets or sets the subtitle (or description) for the feed file
        /// </summary>
        [XmlElement("subtitle")]
        public AtomContent Subtitle
        {
            get { return subtitle; }
            set { subtitle = value; }
        }

        private string language = "en-us";

        /// <summary>
        /// Gets or sets the language of the feed file
        /// </summary>
        [XmlAttribute("xml:lang")]
        public string Language
        {
            get { return language; }
            set { language = value; }
        }
	
    }

    /// <summary>
    /// Describes an entry element in an Atom feed file.
    /// </summary>
    [XmlInclude(typeof(PackInstallerToolItem))]
    [Serializable]
    public class AtomItem
    {
        private Collection<AtomCategory> categories = new Collection<AtomCategory>();

        /// <summary>
        /// Gets or sets the categories assigned to this entry
        /// </summary>
        [XmlElement("category")]
        public Collection<AtomCategory> Categories
        {
            get { return categories; }
            set { categories = value; }
        }

        private Collection<AtomPerson> authors = new Collection<AtomPerson>();

        /// <summary>
        /// Gets or sets the authors for the entry
        /// </summary>
        [XmlElement("author")]
        public Collection<AtomPerson> Authors
        {
            get { return authors; }
            set { authors = value; }
        }

        private DateTime lastUpdate;

        /// <summary>
        /// Gets or sets the last update for the entry
        /// </summary>
        [XmlElement("updated", IsNullable = false, Type = typeof(DateTime))]
        public DateTime LastUpdate
        {
            get { return lastUpdate; }
            set { lastUpdate = value; }
        }

        private string id;

        /// <summary>
        /// Gets or sets the id for this entry. Entries with the same id will be considered as one item.
        /// </summary>
        [XmlElement("id", IsNullable = false)]
        public string Id
        {
            get { return id; }
            set { id = value; }
        }

        private Collection<AtomLink> links = new Collection<AtomLink>();

        /// <summary>
        /// Gets or sets the links associated with this entry
        /// </summary>
        [XmlElement("link")]
        public Collection<AtomLink> Links
        {
            get { return links; }
            set { links = value; }
        }

        private Collection<AtomPerson> contributors = new Collection<AtomPerson>();

        /// <summary>
        /// List of contributors for this feed entry
        /// </summary>
        [XmlElement("contributor")]
        public Collection<AtomPerson> Contributors
        {
            get { return contributors; }
            set { contributors = value; }
        }

        private string title;

        /// <summary>
        /// Gets or sets the title for the feed entry
        /// </summary>
        [XmlElement("title")]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        private AtomContent content;

        /// <summary>
        /// Gets or sets the content of the entry
        /// </summary>
        [XmlElement("content")]
        public AtomContent Content
        {
            get { return content; }
            set { content = value; }
        }
	
	
    }

    /// <summary>
    /// Describes category element for atom feed
    /// </summary>
    [Serializable]
    public class AtomCategory
    {
        private string term;

        /// <summary>
        /// Gets or sets the category name
        /// </summary>
        [XmlAttribute("term")]
	    public string Term
	    {
		    get { return term;}
		    set { term = value;}
	    }
    
	    private string label;

        /// <summary>
        /// Gets or sets the friendly name for the category
        /// </summary>
        [XmlAttribute("label")]
	    public string Label
	    {
		    get { return label;}
		    set { label = value;}
	    }

        private string scheme;

        /// <summary>
        /// Gets or sets the scheme for the category
        /// </summary>
        [XmlAttribute("scheme")]
        public string Scheme
        {
            get { return scheme; }
            set { scheme = value; }
        }

        /// <summary>
        /// Required empty constructor for serialization
        /// </summary>
        public AtomCategory()
        {
        }

        /// <summary>
        /// Creates an atom category object
        /// </summary>
        /// <param name="term">Term for this category</param>
        /// <param name="label">Friendly name for the category entry (optional)</param>
        /// <param name="scheme">Scheme information for the category entry (optional)</param>
        public AtomCategory(string term, string label, string scheme)
        {
            this.term = term;
            this.label = label;
            this.scheme = scheme;
        }
    }

    /// <summary>
    /// Describes author element for atom feed
    /// </summary>
    [Serializable]
    public class AtomPerson
    {
        private string name;

        /// <summary>
        /// Gets or sets the name of the contact
        /// </summary>
        [XmlElement("name")]
        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        private string uri;

        /// <summary>
        /// Gets or sets the URL related to the contact
        /// </summary>
        [XmlElement("uri")]
        public string Uri
        {
            get { return uri; }
            set { uri = value; }
        }

        private string email;

        /// <summary>
        /// Gets or sets the contacts email
        /// </summary>
        [XmlElement("email")]
        public string Email
        {
            get { return email; }
            set { email = value; }
        }

        /// <summary>
        /// Empty constructor required for serialization
        /// </summary>
        public AtomPerson()
        {
        }

        /// <summary>
        /// Creates a new peron entry containing name field only
        /// </summary>
        /// <param name="name">Name of the person/entity</param>
        public AtomPerson(string name)
        {
            this.name = name;
        }
    }

    /// <summary>
    /// Describes link element for atom feed
    /// </summary>
    [Serializable]
    public class AtomLink
    {
        private string reference;

        /// <summary>
        /// Gets or sets the reference for this link
        /// </summary>
        [XmlAttribute("href")]
        public string Reference
        {
            get { return reference; }
            set { reference = value; }
        }

        private string title;

        /// <summary>
        /// Gets or sets the title of the link
        /// </summary>
        [XmlAttribute("title")]
        public string Title
        {
            get { return title; }
            set { title = value; }
        }

        /// <summary>
        /// Gets or sets the relation of the link to its parent
        /// </summary>
        private string relation = String.Empty;

        [XmlAttribute("rel")]
        public string Relation
        {
            get { return relation; }
            set { relation = value; }
        }

        /// <summary>
        /// Required constructor for serialization
        /// </summary>
        public AtomLink()
        {
        }

        /// <summary>
        /// Creates a new atom link object
        /// </summary>
        /// <param name="reference">URL reference of the link</param>
        /// <param name="title">Title of the link (optional)</param>
        /// <param name="relation">Relation of the link to the parent object (optional)</param>
        public AtomLink(string reference, string title, string relation)
        {
            this.relation = relation;
            this.title = title;
            this.reference = reference;
        }
    }

    /// <summary>
    /// Describes the content element for atom feed
    /// </summary>
    [Serializable]
    public class AtomContent
    {

        public enum AtomContentType { text, xhtml };

        private AtomContentType type = AtomContentType.text;

        /// <summary>
        /// Gets or sets the type of the content
        /// </summary>
        [XmlAttribute("type")]
        public AtomContentType Type
        {
            get { return type; }
            set { type = value; }
        }

        private string text;

        /// <summary>
        /// Gets or sets the content (must be parsed according to type specified in Type property)
        /// </summary>
        [XmlText()]
        public string Text
        {
            get { return text; }
            set { text = value; }
        }

        /// <summary>
        /// Required empty constructor for serialization
        /// </summary>
        public AtomContent()
        {
        }

        /// <summary>
        /// Creates an atom content entry with the specified type and text
        /// </summary>
        /// <param name="type"></param>
        /// <param name="text"></param>
        public AtomContent(AtomContentType type, string text)
        {
            this.type = type;
            this.Text = text;
        }
	}

    /// <summary>
    /// Describes the extensions to atom entry for Pack Installer tool information
    /// </summary>
    [XmlType("packToolType", Namespace = "http://www.w3.org/2005/Atom")]
    [Serializable]
    public class PackInstallerToolItem :AtomItem
    {
        private Guid productGuid = Guid.Empty;

        /// <summary>
        /// Gets or sets the GUID for the product installer
        /// </summary>
        [XmlElement("ProductGUID", IsNullable = false)]
        public Guid ProductGuid
        {
            get { return productGuid; }
            set { productGuid = value; }
        }

        private string version = String.Empty;

        /// <summary>
        /// Gets or sets the version of the product
        /// </summary>
        [XmlElement("VersionString", IsNullable = false)]
        public string Version
        {
            get { return version; }
            set { version = value; }
        }

        private string setupFileName = String.Empty;

        /// <summary>
        /// Gets or sets the filename for the setup file
        /// </summary>
        [XmlElement("SetupFilename", IsNullable = false)]
        public string SetupFileName
        {
            get { return setupFileName; }
            set { setupFileName = value; }
        }

        
        private string setupFileHash = String.Empty;

        /// <summary>
        /// Gets or sets the hash for the setup file for verification
        /// </summary>
        [XmlElement("SetupFileHash", IsNullable = false)]
        public string SetupFileHash
        {
            get { return setupFileHash; }
            set { setupFileHash = value; }
        }

        private string licenseFileName = String.Empty;

        /// <summary>
        /// Gets or sets the filename for the license file
        /// </summary>
        [XmlElement("LicenseAgreementFilename", IsNullable = false)]
        public string LicenseFileName
        {
            get { return licenseFileName; }
            set { licenseFileName = value; }
        }

        private string licenseUrl = String.Empty;

        /// <summary>
        /// Gets or sets the URL to retrieve the license file
        /// </summary>
        [XmlElement("LicenseAgreementURL", IsNullable = false)]
        public string LicenseUrl
        {
            get { return licenseUrl; }
            set { licenseUrl = value; }
        }

        private bool hideTool;

        /// <summary>
        /// Gets or sets whether this tool should be hidden in Pack Installer
        /// </summary>
        [XmlElement("HideThisTool", IsNullable = false)]
        public bool HideTool
        {
            get { return hideTool; }
            set { hideTool = value; }
        }

        /// <summary>
        /// Gets or sets the URL for the tool homepage
        /// </summary>
        [XmlIgnore()]
        public string HomePageUrl
        {
            get
            {
                string url = null;
                foreach (AtomLink link in this.Links)
                {
                    if (!String.IsNullOrEmpty(link.Relation) && link.Relation.Equals("related", StringComparison.InvariantCultureIgnoreCase))
                    {
                        url = link.Reference;
                        break;
                    }
                }
                return url;
            }
            set
            {
                foreach (AtomLink link in this.Links)
                {
                    if (!String.IsNullOrEmpty(link.Relation) &&  link.Relation.Equals("related", StringComparison.InvariantCultureIgnoreCase))
                    {
                        link.Reference = value;
                        return;
                    }
                }
                // Not found
                this.Links.Add(new AtomLink(value, String.Empty, "related"));
            }
        }

        /// <summary>
        /// Returns whether the tool item is provided by Microsoft or another company.
        /// </summary>
        [XmlIgnore()]
        public bool IsMicrosoftProvided
        {
            get
            {
                bool result = false;
                foreach (AtomPerson contributor in this.Contributors)
                {
                    if (!String.IsNullOrEmpty(contributor.Name) && contributor.Name.Equals("Microsoft Corporation", StringComparison.InvariantCultureIgnoreCase))
                    {
                        result = true;
                        break;
                    }
                }
                return result;
            }
        }

        /// <summary>
        /// Gets or sets the default pack name that this tool will be listed in.
        /// </summary>
        [XmlIgnore()]
        public string DefaultPackName
        {
            get
            {
                string name = String.Empty;
                foreach (AtomCategory category in this.Categories)
                {
                    if (category.Scheme != null && category.Scheme.Equals("PackCategory", StringComparison.InvariantCultureIgnoreCase))
                    {
                        name = category.Term;
                        break;
                    }
                }
                return name;
            }
            set
            {
                foreach (AtomCategory category in this.Categories)
                {
                    if (category.Scheme != null && category.Scheme.Equals("PackCategory", StringComparison.InvariantCultureIgnoreCase))
                    {
                        category.Term = value;
                        break;
                    }
                }
                // Not found
                this.Categories.Add(new AtomCategory(value, null, "PackCategory"));
            }
        }

        /// <summary>
        /// Gets or sets the URL for the setup file. Set SetupFileName property before setting this.
        /// </summary>
        [XmlIgnore()]
        public string SetupUrl
        {
            get
            {
                string url = null;
                foreach (AtomLink link in this.Links)
                {
                    if (link.Relation != null && link.Relation.Equals("enclosure", StringComparison.InvariantCultureIgnoreCase))
                    {
                        url = link.Reference;
                        break;
                    }
                }
                return url;
            }
            set
            {
                foreach (AtomLink link in this.Links)
                {
                    if (link.Relation != null && link.Relation.Equals("enclosure", StringComparison.InvariantCultureIgnoreCase))
                    {
                        link.Reference = value;
                        link.Title = this.SetupFileName;
                        return;
                    }
                }
                // Not found
                this.Links.Add(new AtomLink(value, this.SetupFileName, "enclosure"));
            }
        }

    }
}
